﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Audio;

namespace Comp376A2_9609695
{
    /// <summary>
    /// The class that represents Dig Dug, ie: the player.
    /// </summary>
    public class DigDug : Updateable, Drawable, Solid
    {
        public enum Direction : short { Left = 0, Up, Right, Down };
        public Vector2 position;
        // Setting the animation string set the animation in the attached spritesheet
        public String animation
        {
            get { return animName; }
            set
            {
                if (spritesheet.getAnimList().Contains(value))
                {
                    if (animName != value)
                    {
                        animName = value;
                        animFrames = spritesheet.getAnimation(animName);
                        frame = 0;
                        frameTime = 0;
                    }
                }
            }
        }

        private Vector2 velocity;
        private int speed;
        private Direction facing;
        public Direction Facing { get { return facing; } }
        public enum State { Moving, Digging, Pumping, Throwing, Squished, Dead, Start };
        public State state;
        private SpriteSheet spritesheet;
        private int frame;
        private float frameTime;
        private List<int> animFrames;
        private String animName;
        private int dug;
        private int frameLength;
        private Pump pumpObj;
        private int timer = 0;
        // Returns the bounding box of the attached pump
        public Rectangle pumpBox
        {
            get
            {
                switch (pumpObj.facing)
                {
                    case Direction.Left:
                        return new Rectangle((int)pumpObj.position.X-3, (int)pumpObj.position.Y+1, 3, 5);
                    case Direction.Right:
                        return new Rectangle((int)pumpObj.position.X+16, (int)pumpObj.position.Y+1, 3, 5);
                    case Direction.Up:
                        return new Rectangle((int)pumpObj.position.X + 1, (int)pumpObj.position.Y-3, 5, 3);
                    case Direction.Down:
                        return new Rectangle((int)pumpObj.position.X + 1, (int)pumpObj.position.Y+16, 5, 3);
                    default:
                        return new Rectangle();
                }
            }
        }

        protected Cue soundCue;
        protected SoundBank bank;
        // When the soundbank is set, the sound cue is set to "throwpump"
        public SoundBank soundbank
        {
            get { return bank; }
            set { bank = value; soundCue = soundbank.GetCue("throwpump"); }
        }

        public DigDug(SpriteSheet spritesheet)
        {
            position = new Vector2(0, 0);
            velocity = new Vector2(0, 0);
            speed = 10;
            facing = Direction.Right;
            this.spritesheet = spritesheet;
            animName = spritesheet.getAnimList().First();
            animFrames = spritesheet.getAnimation(animName);
            frame = 0;
            frameTime = 0;
            frameLength = 75;
            pumpObj = new Pump((int)position.X, (int)position.Y, Direction.Left);
            pumpObj.kill();
        }

        public void update(GameTime time, GameController control)
        {
            Level level = control.level;
            String newAnim = "";
            Rock.State rockState = Rock.State.Static;
            // If DigDug is squished, he falls with the rock and then loses a life
            if (state == State.Squished)
            {
                byte cellBelow = level.getCellAt((int)position.X - level.X, (int)position.Y - level.Y + 16);
                if ((cellBelow != 0 || (int)(position.Y - level.Y) % 16 != 0) && (position.Y - level.Y) < 192)
                {
                    position.Y += 16 / time.ElapsedGameTime.Milliseconds;
                    cellBelow |= 10;
                    timer = 0;
                }
                else
                {
                    timer += time.ElapsedGameTime.Milliseconds;
                    if (timer >= 500)
                    {
                        state = State.Dead;
                        control.state = GameController.GameState.LoseLife;
                    }
                }
            }
            else if (velocity.X != 0 || velocity.Y != 0)
            {
                int newX = (int) position.X;
                int newY = (int) position.Y;
                if (velocity.X == 1)
                    newX += 16;
                if (velocity.Y == 1)
                    newY += 16;
                newX += (int)velocity.X;
                newY += (int)velocity.Y;
                // Check if DigDug needs to dig into the next tile
                if (level.isFilled(newX, newY))
                {
                    state = State.Digging;
                }
                else state = State.Moving;
                int digX = (int)position.X;
                int digY = (int)position.Y;
                if (facing == Direction.Right || facing == Direction.Left)
                {
                        digX += 8;
                    if (digX % 16 == 0)
                        digX++;
                }
                if (facing == Direction.Down || facing == Direction.Up)
                {
                        digY += 8;
                    if (digY % 16 == 0)
                        digY++;
                }
                // Dig out the next tile
                if (level.dig(digX, digY) && control.state == GameController.GameState.Game)
                    dug++;
                // DigDug earns points for digging far enough
                if (dug >= 4 && control.state == GameController.GameState.Game)
                {
                    dug = 0;
                    control.score += 10;
                }
            }
            int elapsed = time.ElapsedGameTime.Milliseconds+1;
            float dx = velocity.X * speed / elapsed;
            float dy = velocity.Y * speed / elapsed;
            // If a rock is in the next tile, don't dig into it. If the rock is falling, squish DigDug
            Rectangle rockColl = new Rectangle();
            switch (facing)
            {
                case Direction.Up:
                    rockColl = new Rectangle((int)position.X + 1, (int)position.Y - 2, 14, 18);
                    break;
                case Direction.Down:
                    rockColl = new Rectangle((int)position.X + 1, (int)position.Y + 1, 14, 17);
                    break;
                case Direction.Left:
                    rockColl = new Rectangle((int)position.X - 2, (int)position.Y + 1, 18, 14);
                    break;
                case Direction.Right:
                    rockColl = new Rectangle((int)position.X + 1, (int)position.Y + 1, 17, 14);
                    break;
            }

            
            if(control.collidesRock(rockColl, ref rockState))
            {
                if (rockState == Rock.State.Shaking || rockState == Rock.State.Static)
                {
                    dx = 0;
                    dy = 0;
                }
                
                else if (rockState == Rock.State.Falling)
                {
                    state = State.Squished;
                }
            }
            position.X += dx;
            position.Y += dy;

            // Keep DigDug in the screen
            if (position.X < level.X)
                position.X = level.X;
            if (position.X > level.X + level.Width - 16)
                position.X = level.X + level.Width - 16;
            if (position.Y < level.Y)
                position.Y = level.Y;
            if (position.Y > level.Y + level.Height - 16)
                position.Y = level.Y + level.Height - 16;

            // Set DigDug's animation depending on his state
            if (state == State.Dead)
            {
                newAnim = "dead";
                frameLength = 400;
            }
            else if (state != State.Start)
            {

                frameLength = 75;
                if (state == State.Digging)
                {
                    newAnim = "dig";
                    frameLength = 75;
                }
                if (state == State.Pumping)
                {
                    if (animName.Substring(0,5) == "throw")
                        pumpObj.stop();
                    newAnim = "pump";
                    frameLength = 500;
                    if (pumpObj.isDead)
                    {
                        state = State.Moving;
                        newAnim = "";
                    }
                }
                if (state == State.Throwing)
                {
                    if (!soundCue.IsPlaying)
                    {
                        soundCue = bank.GetCue("throwpump");
                        soundCue.Play();
                    }
                    newAnim = "throw";
                    if (pumpObj.isDead)
                    {
                        state = State.Moving;
                        newAnim = "";
                    }
                }
                if (facing == Direction.Left)
                {
                    newAnim += "left";
                }
                if (facing == Direction.Right)
                {
                    newAnim += "right";
                }
                if (facing == Direction.Up)
                {
                    newAnim += "up";
                }
                if (facing == Direction.Down)
                {
                    newAnim += "down";
                }
            }
            animation = newAnim;

            // If DigDug is moving or dead, play the animation
            if (velocity.X != 0 || velocity.Y != 0 || state == State.Dead)
            {
                frameTime += elapsed;
                if (frameTime >= frameLength)
                {
                    frame = (frame + 1) % animFrames.Count;
                    frameTime -= frameLength;
                }
            }
            // Pumping takes care of its own animation so only reset the frames if DigDug is not pumping
            else if (state != State.Pumping)
            {
                frame = 0;
                frameTime = 0;
            }
            velocity = new Vector2(0, 0);

            // If the pump is not being thrown, stop the sound
            if (soundCue.IsPlaying && soundCue.Name == "throwpump" && state != State.Throwing)
                soundCue.Stop(AudioStopOptions.Immediate);
             if (state != State.Throwing && state != State.Pumping && !pumpObj.isDead)
                pumpObj.kill();
        }

        public Texture2D getImage()
        {
            return spritesheet.image;
        }

        public void draw(SpriteBatch renderer)
        {
            Rectangle sprite = spritesheet.getSprite(animFrames.ElementAt(frame));
            Rectangle drawRect = new Rectangle((int)position.X, (int)position.Y, sprite.Width, sprite.Height);
            // If DigDug is squished, the sprite is also squished
            if (state == State.Squished)
            {
                drawRect.Y += sprite.Height - 4;
                drawRect.Height = 4;
            }
            renderer.Draw(spritesheet.image, drawRect, sprite, Color.White);
        }

        public bool collides(Rectangle box)
        {
            // DigDug doesn't handle his own collisions
            return false;
        }

        public void move(Direction dir, GameController control)
        {
            Level level = control.level;
            // Make sure that DigDug can only turn into a tile, not halfway into one
            Direction tryDir = dir;
            Direction moveDir = tryDir;
            if ((tryDir == Direction.Left || tryDir == Direction.Right)
                && ((int)(position.Y - level.Y) % 16) != 0)
            {
                moveDir = facing;
            }
            if ((tryDir == Direction.Up || tryDir == Direction.Down)
                && ((int)(position.X - level.X) % 16) != 0)
            {
                moveDir = facing;
            }
            facing = moveDir;
            switch (moveDir)
            {
                case Direction.Left:
                    velocity.X = -1;
                    break;
                case Direction.Right:
                    velocity.X = 1;
                    break;
                case Direction.Up:
                    velocity.Y = -1;
                    break;
                case Direction.Down:
                    velocity.Y = 1;
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        /// Force DigDug to face a direction
        /// </summary>
        /// <param name="dir">The direction to face</param>
        public void faceDir(Direction dir)
        {
            facing = dir;
        }

        /// <summary>
        /// Have DigDug throw his pump
        /// </summary>
        /// <param name="control">The controller object</param>
        /// <param name="sheet">The spritesheet to use for the pump</param>
        public void throwPump(GameController control, SpriteSheet sheet)
        {
            int startX = (int)position.X;
            int startY = (int)position.Y;
            pumpObj = new Pump(startX, startY, facing);
            pumpObj.spritesheet = sheet;
            state = State.Throwing;
            control.addObject(pumpObj);
        }

        /// <summary>
        /// Have DigDug pump
        /// </summary>
        public void pump()
        {
            if (state == State.Pumping)
            {
                frame = (frame + 1) % animFrames.Count;
                pumpObj.pump();
            }
        }

        /// <summary>
        /// Attach DigDug's pump to a monster
        /// </summary>
        /// <param name="monster">The monster to attach the pump to</param>
        public void attach(Monster monster)
        {
            pumpObj.attach(monster);
        }

        /// <summary>
        /// Detach DigDug's pump from a monster
        /// </summary>
        /// <param name="monster">The monster to detach the pump from</param>
        public void detach(Monster monster)
        {
            pumpObj.detach(monster);
            pumpObj.kill();
            state = State.Moving;
        }
    }
}
